﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
using System.Linq;
using Microsoft.VisualStudio.TextManager.Interop;
using System.Diagnostics;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Text.Editor;


namespace Marias.Extensions.Csharp {


    [Export(typeof(IClassifierProvider))]
    [ContentType("CSharp")]
    internal class IdentifierClassifierProvider : IClassifierProvider {
        [Import]
        internal IClassificationTypeRegistryService _ClassRegistry = null; 
        [Import]
        internal IClassifierAggregatorService _Aggregator = null;
        [Import]
        internal SVsServiceProvider _ServiceProvider = null;
        [Import]
        internal ITextDocumentFactoryService _TextDocService = null;


        internal static bool _IgnoreRequest = false;

        public IdentifierClassifierProvider() {
        }

        public IClassifier GetClassifier(ITextBuffer p_buffer) {
            if (_IgnoreRequest)
                return null;
            try
            {
                ITextDocument i_doc;
                if (_TextDocService.TryGetTextDocument(p_buffer, out i_doc))
                {
                    Type i_type = Type.GetTypeFromCLSID(new Guid("694DD9B6-B865-4C5B-AD85-86356E9C88DC"));
                    IVsContainedLanguageFactory i_fact = (IVsContainedLanguageFactory)_ServiceProvider.GetService(i_type);
                    _IgnoreRequest = true;
                    return p_buffer.Properties.GetOrCreateSingletonProperty<IdentifiersClassifier>(delegate { return new IdentifiersClassifier(_Aggregator.GetClassifier(p_buffer), _ClassRegistry, i_doc.FilePath); });
                } else
                    return null;
            } finally
            {
                _IgnoreRequest = false;
            }
        }
    }



    class IdentifiersClassifier : IClassifier {

        IClassificationType _ParameterType;
        IClassificationType _LocalVariableType;
        IClassifier _Aggregator;
        IClassificationTypeRegistryService _Registry;
        CodeAnalyzer _Analyzer;
        double _ParseTime = 0;
        bool _IsRunning = false;
        string _FileName;


        internal IdentifiersClassifier(IClassifier p_aggregator, IClassificationTypeRegistryService p_registry,
            string p_FileName) {
            _ParameterType = p_registry.GetClassificationType("ProcedureParameter");
            _LocalVariableType = p_registry.GetClassificationType("LocalVariable");
            _Aggregator = p_aggregator;
            _Registry = p_registry;
            _FileName = p_FileName;
            _Analyzer = new CodeAnalyzer(p_FileName);
        }

        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span) {
            if (_IsRunning)
                return new List<ClassificationSpan>();
            else
            {
                try
                {
                    _IsRunning = true;
                    return GetMyClassificationSpans(span);
                } finally
                {
                    _IsRunning = false;
                }
            }
        }

        public IList<ClassificationSpan> GetMyClassificationSpans(SnapshotSpan span) {
            Stopwatch sw = Stopwatch.StartNew();
            List<ClassificationSpan> i_result = new List<ClassificationSpan>();
                        
            var classifiedSpans = from cspan in _Aggregator.GetClassificationSpans(span)
                                  let name = cspan.ClassificationType.Classification.ToLower()
                                  let position = cspan.Span.Start.Position
                                  let line = cspan.Span.Snapshot.GetLineFromPosition(position)
                                  let linenumber = line.LineNumber
                                  let colnumber = position - line.Start
                                  where name.Contains("identifier")
                                  select new SpanDescriptor
                                  {
                                      ColNumber = colnumber,
                                      LineNumber = linenumber,
                                      Span = cspan.Span,
                                      Text = cspan.Span.GetText()
                                  };

            foreach (SpanDescriptor d in classifiedSpans)
            {
                NodeType i_IdentType = _Analyzer.IsParameter(_FileName, d);
                switch (i_IdentType)
                {
                    case NodeType.Unknown:
                        break;
                    case NodeType.Parameter:
                         i_result.Add(new ClassificationSpan(d.Span, _ParameterType));
                        break;
                    case NodeType.LocalVariable:
                        i_result.Add(new ClassificationSpan(d.Span, _LocalVariableType));
                        break;
                }
            }

            _ParseTime += sw.ElapsedMilliseconds;
            Debug.WriteLine("Classifier Last: " + sw.ElapsedMilliseconds.ToString("N0") + " Total: " + _ParseTime.ToString("N0"));
            return i_result;
        }

        public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;

  }

    public class SpanDescriptor {
        public string Text { get; set; }
        public int ColNumber { get; set; }
        public int LineNumber { get; set; }
        public SnapshotSpan Span { get; set; }
    }

    public enum NodeType {
        Unknown,
        Parameter,
        LocalVariable
    }

}


